#include "MyUart.h"


//*********************************************************************//
//				2025/6/17
//(严重bug上电就进空闲中断不停止，无相关资料，留作标记，有解决方案再改)
//暂时找不到原因，等我用STM32跑一遍程序试试
//				2025/6/19
//使用STM32出现同一问题，使用示波器排查后发现问题： 公司漏电！！！
//*********************************************************************//


uint8_t U0_RxBuff[UART0_RX_SIZE]={0};//定义接收缓存数组
uint8_t U0_TxBuff[UART0_TX_SIZE]={0};//定义发送缓存数组
UCB_CB 	BL_CB;											 //bootloader控制块

//初始化USART0
//参数：bandrate 波特率
//无返回值
void MyUsart0_Init(uint32_t bandrate)
{
	//使能USART0外设时钟
	rcu_periph_clock_enable(RCU_USART0);
	//使能UASRT0 Tx/Rx引脚时钟
	rcu_periph_clock_enable(RCU_GPIOA);
	
	//Usart0 Tx引脚配置
	gpio_output_options_set(GPIOA,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_9);
	gpio_mode_set(GPIOA,GPIO_MODE_AF,GPIO_PUPD_PULLUP,GPIO_PIN_9);
	gpio_af_set(GPIOA,GPIO_AF_1,GPIO_PIN_9);
	
	//Usart0 Rx引脚配置
	gpio_mode_set(GPIOA,GPIO_MODE_AF,GPIO_PUPD_PULLUP,GPIO_PIN_10);
	gpio_af_set(GPIOA,GPIO_AF_1,GPIO_PIN_10);
	
	//恢复UASRT0缺省配置，防止出现意外情况
	usart_deinit(USART0);
	//设置USART0波特率
	usart_baudrate_set(USART0,bandrate);
	//设置USART0奇偶校验
	usart_parity_config(USART0,USART_PM_NONE);
	//设置USART0数据长度
	usart_word_length_set(USART0,USART_WL_8BIT);
	//设置USART0停止位
	usart_stop_bit_set(USART0,USART_STB_1BIT);
	//使能USART0发送
	usart_transmit_config(USART0,USART_TRANSMIT_ENABLE);
	//使能USART0接收
	usart_receive_config(USART0,USART_RECEIVE_ENABLE);
	//使能USART0接收DMA
	usart_dma_receive_config(USART0,USART_DENR_ENABLE);
	
	//使能USART0中断，并分配优先级
	nvic_irq_enable(USART0_IRQn,0);
	
	
	//初始化接收指针
	BootLoader_Rx_Ptr_Init();
	//初始化DMA
	MyUsart0_DMA_Init();
	
	//使能USART0
	usart_enable(USART0);
	
	while(RESET == usart_flag_get(USART0,USART_FLAG_IDLE));
	usart_flag_clear(USART0,USART_FLAG_IDLE);
	//使能USART0空闲中断
	usart_interrupt_enable(USART0,USART_INT_IDLE);
}

//UASRT0的DMA初始化
//使用DMA转运bootloader接收到的程序
//无参
//无返回值
void MyUsart0_DMA_Init(void)
{
	//声明DMA初始化结构体
	dma_parameter_struct dma_init_structure={0};
	
	//开启DMA时钟
	rcu_periph_clock_enable(RCU_DMA);

	//恢复DMA 通道2 缺省配置
	dma_deinit(DMA_CH2);
	//设置DMA 外设地址 
	dma_init_structure.periph_addr=(uint32_t)&USART_RDATA(USART0);
	//设置DMA 外设地址不递增
	dma_init_structure.periph_inc= DMA_PERIPH_INCREASE_DISABLE;
	//设置DMA 转运数据宽度
	dma_init_structure.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
	//设置DMA 存储器地址
	dma_init_structure.memory_addr=(uint32_t)U0_RxBuff;
	//设置DMA 存储器地址递增
	dma_init_structure.memory_inc= DMA_MEMORY_INCREASE_ENABLE;
	//设置DMA 存储器数据宽度
	dma_init_structure.memory_width=DMA_MEMORY_WIDTH_8BIT;
	//设置DMA 优先级
	dma_init_structure.priority=DMA_PRIORITY_HIGH;
	//设置DMA 转运数据数量，因为需要空闲中断，所以要大于实际转运数量
	dma_init_structure.number = UART0_RX_MAX +1;
	//DMA数据转运方向，外设到存储器
	dma_init_structure.direction=DMA_PERIPHERAL_TO_MEMORY;

	//初始化DMA
	dma_init(DMA_CH2,&dma_init_structure);
	
	//不使用连续模式
	dma_circulation_disable(DMA_CH2);
	
	dma_memory_to_memory_disable(DMA_CH2);
	//DMA通道使能
	dma_channel_enable(DMA_CH2);
	
}

//BootLoader接收指针初始化
void BootLoader_Rx_Ptr_Init(void)
{
	//计数清零
	BL_CB.URxCounter = 0;
	//输入指针指向起始位置
	BL_CB.URxDataIn  = &BL_CB.URxDataPtr[0];
	//输出指针指向起始位置
	BL_CB.URxDataOut = &BL_CB.URxDataPtr[0];
	//尾指针指向边界，用于溢出判断
	BL_CB.URxDataEnd = &BL_CB.URxDataPtr[NUM-1];
	//起始点设置为数组首元素
	BL_CB.URxDataIn->start = U0_RxBuff;
}


//串口接收空闲中断
void USART0_IRQHandler(void)
{
	//如果是串口接收空闲中断
	if(usart_interrupt_flag_get(USART0,USART_INT_FLAG_IDLE) != 0)
	{
		//失能DMA通道，在处理时不接收数据
		dma_channel_disable(DMA_CH2);
		//记录累加值 += 总量(UART0_RX_MAX+1)-dma转运剩余值
		BL_CB.URxCounter +=  (UART0_RX_MAX+1) -dma_transfer_number_get(DMA_CH2);
		//记录本次数据尾部
		BL_CB.URxDataIn->end = &U0_RxBuff[BL_CB.URxCounter-1];
		//输入指针指向下一块
		BL_CB.URxDataIn++;
		//越界判断 如果已经到分区结构体尾部
		if(BL_CB.URxDataIn == BL_CB.URxDataEnd)
		{
			//重新指向分区结构体头部
			BL_CB.URxDataIn  = &BL_CB.URxDataPtr[0];
		}
		//如果剩余内存还够存储一次最大数据片
		if(UART0_RX_MAX <= (UART0_RX_SIZE-BL_CB.URxCounter))
		{
			//接着上次数据片尾部继续存储
			BL_CB.URxDataIn->start = &U0_RxBuff[BL_CB.URxCounter];
		}
		else
		{
			//将数据存储到缓冲区开头处
			BL_CB.URxDataIn->start = &U0_RxBuff[0];
			BL_CB.URxCounter=0;
		}
		//关闭DMA重新设置DMA剩余数量为最大值
		dma_transfer_number_config(DMA_CH2,UART0_RX_MAX+1);
		//重新设置DMA 存储器数据地址
		dma_memory_address_config(DMA_CH2,(uint32_t)BL_CB.URxDataIn->start);
		
		//重新使能DMA
		dma_channel_enable(DMA_CH2);
		
		//清除串口接收空闲中断
		usart_interrupt_flag_clear(USART0,USART_INT_FLAG_IDLE);	
	}
}

//可变长串口重定向
void U0_printf(char * format,...)
{
	//声明一个 va_list 变量，用于存储可变参数列表的信息。
	va_list listdata;
	//使用 va_start 宏初始化 va_list 变量
	va_start(listdata,format);
	//使用参数列表发送格式化输出到字符串
	vsprintf((char*)U0_TxBuff,format,listdata);
	//使用 va_end 宏清理 va_list 变量
	va_end(listdata);
	//USART0循环发送数据
	for(uint16_t i=0;i<strlen((const char*)U0_TxBuff);i++)
	{
		//等待发送缓冲区空
		while(usart_flag_get(USART0,USART_FLAG_TBE) != 1);
		//发送数据
		usart_data_transmit(USART0,U0_TxBuff[i]);
	}
	//等待发送缓冲区空
	while(usart_flag_get(USART0,USART_FLAG_TBE) != 1);
}

//串口重定向
int fputc(int ch ,FILE* f)
{
	usart_data_transmit(USART0,(unsigned char)ch);
	while(usart_flag_get(USART0,USART_FLAG_TBE) != 1);
	return ch;
}


